/****************************************************************************
**
** This file is part of the KD Chart library.
**
** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
**
** SPDX-License-Identifier: MIT
**
****************************************************************************/

#include "mainwindow.h"
#include "framewidget.h"

#include <KDChartAbstractCoordinatePlane>
#include <KDChartBackgroundAttributes>
#include <KDChartChart>
#include <KDChartDataValueAttributes>
#include <KDChartFrameAttributes>
#include <KDChartLegend>
#include <KDChartLineAttributes>
#include <KDChartLineDiagram>
#include <KDChartMarkerAttributes>
#include <KDChartTextAttributes>
#include <KDChartThreeDLineAttributes>

#include <QDebug>
#include <QElapsedTimer>
#include <QPainter>

using namespace KDChart;

static void drawIntoPixmap(QPixmap &pix, const QSize &size, KDChart::Chart *chart)
{
    pix = QPixmap(size);
    pix.fill(Qt::white);
    QPainter painter(&pix);
    chart->paint(&painter, QRect(0, 0, size.width(), size.height()));
}

// When set, this example uses FrameWidget which uses Chart::paint to paint itself.
// When not set, this example uses a Chart widget directly.
#define USE_FRAME_WIDGET 1

MainWindow::MainWindow(QWidget *parent)
    : QWidget(parent)
{
    setupUi(this);

    auto *chartLayout = new QHBoxLayout(chartFrame);
#ifdef USE_FRAME_WIDGET
    auto *chartFrameWidget = new FrameWidget();
    chartLayout->addWidget(chartFrameWidget);
#endif
    hSBar->setVisible(false);
    vSBar->setVisible(false);

    // Set up the diagram
    m_lines = new LineDiagram();

    //    updateData(":/test");
    m_model.loadFromCSV(QString(":/test"));
    m_lines->setModel(&m_model);

    auto *xAxis = new CartesianAxis(m_lines);
    auto *yAxis = new CartesianAxis(m_lines);
    auto *axisTop = new CartesianAxis(m_lines);
    auto *axisRight = new CartesianAxis(m_lines);
    xAxis->setPosition(KDChart::CartesianAxis::Bottom);
    yAxis->setPosition(KDChart::CartesianAxis::Left);
    axisTop->setPosition(KDChart::CartesianAxis::Top);
    axisRight->setPosition(KDChart::CartesianAxis::Right);

    m_lines->addAxis(xAxis);
    m_lines->addAxis(yAxis);
    m_lines->addAxis(axisTop);
    m_lines->addAxis(axisRight);

    m_chart = new Chart();
    // m_chart->setGlobalLeading(10,10,10,10); // by default there is no leading

#ifdef USE_FRAME_WIDGET
    chartFrameWidget->setChart(m_chart);
    // make sure, we re-draw after changing one of the chart's properties
    connect(m_chart, &Chart::propertiesChanged,
            chartFrameWidget, QOverload<>::of(&QWidget::update));
#else
    chartLayout->addWidget(m_chart);
#endif

    m_chart->coordinatePlane()->replaceDiagram(m_lines);

    for (int iColumn = 0; iColumn < m_lines->model()->columnCount(); ++iColumn) {
        QPen pen(m_lines->pen(iColumn));
        pen.setWidth(4);
        m_lines->setPen(iColumn, pen);
    }

    FrameAttributes faChart(m_chart->frameAttributes());
    faChart.setPen(QPen(QColor(0x60, 0x60, 0xb0), 8));
    m_chart->setFrameAttributes(faChart);

    BackgroundAttributes baChart(m_chart->backgroundAttributes());
    baChart.setVisible(true);
    baChart.setBrush(QColor(0xd0, 0xd0, 0xff));
    m_chart->setBackgroundAttributes(baChart);

    // Set up the legend
    m_legend = new Legend(m_lines, m_chart);

    m_legend->setPosition(Position::South);
    m_legend->setAlignment(Qt::AlignRight);
    m_legend->setShowLines(false);
    m_legend->setTitleText(tr("Legend"));
    m_legend->setOrientation(Qt::Horizontal);

    const QColor legendColor(0xff, 0xe0, 0x80);
    FrameAttributes faLegend(m_legend->frameAttributes());
    faLegend.setPen(QPen(legendColor, 1));
    m_legend->setFrameAttributes(faLegend);

    BackgroundAttributes baLegend(m_legend->backgroundAttributes());
    baLegend.setVisible(true);
    baLegend.setBrush(legendColor);
    m_legend->setBackgroundAttributes(baLegend);

    m_chart->addLegend(m_legend);

    // m_chart->setGeometry(10,10,50,50);

    // for illustration we paint the same chart at different sizes:
    QSize size1 = QSize(200, 200);
    QSize size2 = QSize(800, 800);
    drawIntoPixmap(m_pix1, size1, m_chart);
    drawIntoPixmap(m_pix2, size2, m_chart);
    m_pix2 = m_pix2.scaled(size1);

    m_smallChart1 = new QLabel(this);
    m_smallChart1->setWindowTitle("200x200");
    m_smallChart1->setPixmap(m_pix1);
    m_smallChart1->setFixedSize(m_pix1.size());
    m_smallChart1->move(width() - m_pix1.width() * 2,
                        height() / 2 - m_pix1.height() - 5);
    m_smallChart1->show();

    m_smallChart2 = new QLabel(this);
    m_smallChart2->setWindowTitle("800x800 scaled down");
    m_smallChart2->setPixmap(m_pix2);
    m_smallChart2->setFixedSize(m_pix2.size());
    m_smallChart2->move(width() - m_pix2.width() * 2,
                        height() / 2 + 5);
    m_smallChart2->show();

    faChart.setPen(QPen(QColor(0xb0, 0xb0, 0xff), 8));
    m_chart->setFrameAttributes(faChart);
}

void MainWindow::updateData(QString data)
{
    QElapsedTimer t;
    t.start();

    m_model.loadFromCSV(data);

    qDebug() << "Time for loading data" << data << "-" << t.elapsed() << "ms";
    t.restart();

    QSize size1 = QSize(200, 200);
    QSize size2 = QSize(800, 800);
    drawIntoPixmap(m_pix1, size1, m_chart);
    drawIntoPixmap(m_pix2, size2, m_chart);

    qDebug() << "Time for drawing pixmap" << data << "-" << t.elapsed() << "ms";
    t.restart();

    m_lines->setModel(&m_model);

    qDebug() << "Time for setting model" << data << "-" << t.elapsed() << "ms";
    t.restart();

    m_smallChart1->setPixmap(m_pix1);
    m_smallChart2->setPixmap(m_pix2);

    m_smallChart1->show();
    m_smallChart2->show();

    qDebug() << "Time for setting pixmap" << data << "-" << t.elapsed() << "ms";
    t.restart();
}

void MainWindow::on_lineTypeCB_currentIndexChanged(int index)
{
    const QString text = lineTypeCB->itemText(index);
    if (text == "Normal")
        m_lines->setType(LineDiagram::Normal);
    else if (text == "Stacked")
        m_lines->setType(LineDiagram::Stacked);
    else if (text == "Percent")
        m_lines->setType(LineDiagram::Percent);
    else
        qWarning(" Does not match any type");
}

void MainWindow::on_paintLegendCB_toggled(bool checked)
{
    m_legend->setVisible(checked);
}

void MainWindow::on_paintValuesCB_toggled(bool checked)
{
    // testing
    const int colCount = m_lines->model()->columnCount();
    for (int iColumn = 0; iColumn < colCount; ++iColumn) {
        QBrush brush = m_lines->model()->headerData(iColumn, Qt::Vertical, DatasetBrushRole).value<QBrush>();
        DataValueAttributes a = m_lines->dataValueAttributes(iColumn);
        if (!paintMarkersCB->isChecked()) {
            MarkerAttributes ma = a.markerAttributes();
            ma.setVisible(false);
            a.setMarkerAttributes(ma);
        }
        TextAttributes ta = a.textAttributes();
        ta.setRotation(0);
        ta.setFont(QFont("Comic", 10));
        ta.setPen(QPen(brush.color()));
        if (checked)
            ta.setVisible(true);
        else
            ta.setVisible(false);

        a.setTextAttributes(ta);
        a.setVisible(true);
        m_lines->setDataValueAttributes(iColumn, a);
    }
}

void MainWindow::on_paintMarkersCB_toggled(bool checked)
{
    // testing
    DataValueAttributes a(m_lines->dataValueAttributes());
    // don't paint the values
    if (!paintValuesCB->isChecked()) {
        TextAttributes ta = a.textAttributes();
        ta.setVisible(false);
        a.setTextAttributes(ta);
    }
    MarkerAttributes ma(a.markerAttributes());
    MarkerAttributes::MarkerStylesMap map;
    map.insert(0, MarkerAttributes::MarkerSquare);
    map.insert(1, MarkerAttributes::MarkerCircle);
    map.insert(2, MarkerAttributes::MarkerRing);
    map.insert(3, MarkerAttributes::MarkerCross);
    map.insert(4, MarkerAttributes::MarkerDiamond);
    ma.setMarkerStylesMap(map);

    switch (markersStyleCB->currentIndex()) {
    case 0: {
        break;
    }
    case 1: {
        ma.setMarkerStyle(MarkerAttributes::MarkerCircle);
        break;
    }
    case 2: {
        ma.setMarkerStyle(MarkerAttributes::MarkerSquare);
        break;
    }
    case 3: {
        ma.setMarkerStyle(MarkerAttributes::MarkerDiamond);
        break;
    }
    case 4: {
        ma.setMarkerStyle(MarkerAttributes::Marker1Pixel);
        break;
    }
    case 5: {
        ma.setMarkerStyle(MarkerAttributes::Marker4Pixels);
        break;
    }
    case 6: {
        ma.setMarkerStyle(MarkerAttributes::MarkerRing);
        break;
    }
    case 7: {
        ma.setMarkerStyle(MarkerAttributes::MarkerCross);
        break;
    }
    case 8: {
        ma.setMarkerStyle(MarkerAttributes::MarkerFastCross);
        break;
    }
    }
    ma.setMarkerSize(QSize(markersWidthSB->value(), markersHeightSB->value()));

    if (checked)
        ma.setVisible(true);
    else
        ma.setVisible(false);

    a.setMarkerAttributes(ma);
    a.setVisible(true);

    // make a special one for certain values
    DataValueAttributes yellowAttributes(a);
    MarkerAttributes yellowMarker(yellowAttributes.markerAttributes());
    yellowMarker.setMarkerColor(Qt::yellow);
    yellowAttributes.setMarkerAttributes(yellowMarker);

    const int rowCount = m_lines->model()->rowCount();
    const int colCount = m_lines->model()->columnCount();
    for (int iColumn = 0; iColumn < colCount; ++iColumn) {
        DataValueAttributes colAttributes(a);
        if (markersStyleCB->currentIndex() == 0) {
            MarkerAttributes ma(colAttributes.markerAttributes());
            ma.setMarkerStyle(ma.markerStylesMap().value(iColumn));
            colAttributes.setMarkerAttributes(ma);
        }
        for (int j = 0; j < rowCount; ++j) {
            QModelIndex index = m_lines->model()->index(j, iColumn, QModelIndex());
            QBrush brush = m_lines->model()->headerData(iColumn, Qt::Vertical, DatasetBrushRole).value<QBrush>();
            qreal value = m_lines->model()->data(index).toReal();
            /* Set a specific color - marker for a specific value */
            if (value == 8) {
                m_lines->setDataValueAttributes(index, yellowAttributes);
            }
        }
        m_lines->setDataValueAttributes(iColumn, colAttributes);
    }
}

void MainWindow::on_markersStyleCB_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    if (paintMarkersCB->isChecked())
        on_paintMarkersCB_toggled(true);
}

void MainWindow::on_markersWidthSB_valueChanged(int i)
{
    Q_UNUSED(i);
    markersHeightSB->setValue(markersWidthSB->value());
    if (paintMarkersCB->isChecked())
        on_paintMarkersCB_toggled(true);
}

void MainWindow::on_markersHeightSB_valueChanged(int i)
{
    Q_UNUSED(i);
    markersWidthSB->setValue(markersHeightSB->value());
    if (paintMarkersCB->isChecked())
        on_paintMarkersCB_toggled(true);
}

void MainWindow::on_displayAreasCB_toggled(bool checked)
{
    const int rowCount = m_lines->model()->rowCount();
    const int colCount = m_lines->model()->columnCount();
    for (int iColumn = 0; iColumn < colCount; ++iColumn) {
        for (int j = 0; j < rowCount; ++j) {
            LineAttributes la(m_lines->lineAttributes(iColumn));
            if (checked) {
                la.setDisplayArea(true);
                la.setTransparency(transparencySB->value());
            } else
                la.setDisplayArea(false);
            m_lines->setLineAttributes(iColumn, la);
        }
    }
}

void MainWindow::on_transparencySB_valueChanged(int alpha)
{
    const int rowCount = m_lines->model()->rowCount();
    const int colCount = m_lines->model()->columnCount();
    for (int iColumn = 0; iColumn < colCount; ++iColumn) {
        for (int j = 0; j < rowCount; ++j) {
            LineAttributes la = m_lines->lineAttributes(iColumn);
            la.setTransparency(alpha);
            m_lines->setLineAttributes(la);
        }
    }
    on_displayAreasCB_toggled(true);
}

void MainWindow::on_zoomFactorSB_valueChanged(qreal factor)
{
    if (factor > 1) {
        hSBar->setVisible(true);
        vSBar->setVisible(true);
    } else {
        hSBar->setValue(500);
        vSBar->setValue(500);
        hSBar->setVisible(false);
        vSBar->setVisible(false);
    }
    m_chart->coordinatePlane()->setZoomFactorX(factor);
    m_chart->coordinatePlane()->setZoomFactorY(factor);
}

void MainWindow::on_hSBar_valueChanged(int hPos)
{
    m_chart->coordinatePlane()->setZoomCenter(QPointF(hPos / 1000.0, vSBar->value() / 1000.0));
    m_chart->update();
}

void MainWindow::on_vSBar_valueChanged(int vPos)
{
    m_chart->coordinatePlane()->setZoomCenter(QPointF(hSBar->value() / 1000.0, vPos / 1000.0));
}

void MainWindow::on_savePB_clicked()
{
    qDebug() << "Painting into PNG";
    QPixmap qpix(600, 600);
    QPainter painter(&qpix);
    painter.setBrush(Qt::white);
    painter.fillRect(0, 0, 600, 600, Qt::white);
    m_chart->paint(
        &painter,
        QRect(100, 100, 400, 400));
    painter.end();
    qpix.save("kdchart-demo.png", "PNG");
    qDebug() << "Painting into PNG - done";
}

void MainWindow::resizeEvent(QResizeEvent *)
{
    m_smallChart1->move(width() - m_pix1.width() * 2,
                        height() / 2 - m_pix1.height() - 5);
    m_smallChart2->move(width() - m_pix2.width() * 2,
                        height() / 2 + 5);
}
